var canvas = document.getElementById('canvas'),
	context = canvas.getContext('2d'),

	scoreboard = document.getElementById('scoreboard'),
	launchVelocityOutput = document.getElementById('launchVelocityOutput'),
	launchAngleOutput = document.getElementById('launchAngleOutput'),

	elapsedTime = undefined, // 逝去的时间
	launchTime = undefined, // 发射的时间

	score = 0, // 总分
	lastScore = 0, // 最新得分
	lastMouse = { left: 0, top: 0 }, // 鼠标最新按下的位置

	threePointer = false, // 是否是三分球
	needInstructions= true, // 需要指令（说明）

	// 起跳板
	LAUNCHPAD_X = 50,
	LAUNCHPAD_Y = canvas.height - 50,
	LAUNCHPAD_WIDTH = 50,
	LAUNCHPAD_HEIGHT = 12,

	BALL_RADIUS = 8, // 小球半径
	ARENA_LENGTH_IN_METERS = 10, // 将画布当做10米宽的平台
	INITIAL_LAUNCH_ANGLE = Math.PI/4, // 起始发射角度

	launchVelocity = undefined, // 发射速度
	launchAngle = INITIAL_LAUNCH_ANGLE, // 发射角度
	pixelsPerMeter = canvas.width / ARENA_LENGTH_IN_METERS, // 像素/米

// Launch pad........................................................................

	// Painter--起跳板绘制器
	launchPadPainter = {
		LAUNCHPAD_FILL_STYLE: 'rgb(100, 140, 230)',

		paint: function(ledge, context){
			context.save();
			context.fillStyle = this.LAUNCHPAD_FILL_STYLE;
			context.fillRect(LAUNCHPAD_X, LAUNCHPAD_Y, LAUNCHPAD_WIDTH, LAUNCHPAD_HEIGHT);
			context.restore();
		}
	},

	// Sprite--起跳板精灵
	launchPad = new Sprite('launchPad', launchPadPainter),

// Ball...............................................................................

	// Painter--小球绘制器
	ballPainter = {
		BALL_FILL_STYLE: 'rgb(255, 255, 0)',
		BALL_STROKE_STYLE: 'rgba(0, 0, 0, 0.4)',

		paint: function(ball, context){
			context.save();
			
			context.shadowColor = undefined;
			context.lineWidth = 2;
			context.fillStyle = this.BALL_FILL_STYLE;
			context.strokeStyle = this.BALL_STROKE_STYLE;

			context.beginPath();
			context.arc(ball.left, ball.top, ball.radius, 0, Math.PI*2, false);
			context.clip();
			context.fill();
			context.stroke();

			context.restore();
		}
	},

	// Behavior--小球的行为
	lob = {
		lastTime: 0, // 最近记录的时间
		GRAVITY_FORCE: 9.81, // m/s/s

		// 根据重力计算小球的Y速度
		applyGravity: function(elapsed){
			ball.velocityY = (this.GRAVITY_FORCE * elapsed) - (launchVelocity * Math.sin(launchAngle)); 
		},

		// 更新小球的位置，每一帧累加X&&Y方向的距离
		updateBallPosition: function(updateDelta){
			ball.left += ball.velocityX * (updateDelta) * pixelsPerMeter;
			ball.top += ball.velocityY * (updateDelta) * pixelsPerMeter;
		},

		// 当小球脱离画布上方，判断为三分球的第一条件
		checkForThreePointer: function(){
			if(ball.top < 0){
				threePointer = true;
			}
		},

		// 判断小球是否脱离边界（下方&&右侧）
		checkBallBounds: function(){
			if(ball.top > canvas.height || ball.left > canvas.width){
				reset();
			}
		},

		execute: function(ball, context, time){
			var elapsedFrameTime, 
				elapsedFlightTime;

			if(ballInFlight){
				elapsedFrameTime = (time - this.lastTime) / 1000; // 每一帧的时间差
				elapsedFlightTime = (time - launchTime) / 1000; // 从小球发射到现在的时间差

				this.applyGravity(elapsedFlightTime);
				this.updateBallPosition(elapsedFrameTime);
				this.checkForThreePointer();
				this.checkBallBounds();
			}

			this.lastTime = time; // 更新最新时间
		},
	},

	// Sprite--小球精灵
	ball = new Sprite('ball', ballPainter, [lob]),
	
	ballInFlight = false; // 是否是飞行阶段

// Bucket..............................................................................................

	BUCKET_X = 668,
	BUCKET_Y = canvas.height - 100,
	bucketImage = new Image(),

	// Painter--得分桶的绘制器
	bucketPainter = {
		paint: function(sprite, context){
			context.drawImage(bucketImage, BUCKET_X, BUCKET_Y);
		}
	},
	
	// Behavior--捕获小球的行为
	catchBall = {
		// 判断小球是否进桶
		ballInBucket: function(){
			return ball.left > bucket.left + bucket.width/2 &&
				   ball.left < bucket.left + bucket.width &&
				   ball.top > bucket.top &&
				   ball.top < bucket.top + bucket.height/3;
		},

		// 调整得分
		adjustScore: function(){
			lastScore = threePointer ? 3 : 2;
			score += lastScore;
			scoreboard.innerHTML = score;
		},

		execute: function(bucket, context, time){
			if(ballInFlight && this.ballInBucket()){
				this.adjustScore();
				reset();
			}
		}
	},

	// Sprite--得分桶精灵
	bucket = new Sprite('bucket', bucketPainter, [catchBall]);

// Functions................................................................

	// 获得窗口相对canvas的坐标位置
	function windowToCanvas(x, y){
		var bbox = canvas.getBoundingClientRect();

		return {
			x: x - bbox.left * (canvas.width / bbox.width),
			y: y - bbox.top * (canvas.height / bbox.height)
		};
	}

	// 重置小球属性，最新得分归零
	function reset(){
		ball.left = LAUNCHPAD_X + LAUNCHPAD_WIDTH / 2;
		ball.top = LAUNCHPAD_Y - ball.height / 2;
		ball.velocityX = 0;
		ball.velocityY = 0;
		ballInFlight = false;
		needInstructions = false;
		lastScore = 0;
	}

	// 显示提示文字
	function showText(text){
		var metrics;

		context.font = '42px Helvetica';
		metrics = context.measureText(text);

		context.save();

		context.shadowColor = undefined;
		context.strokeStyle = 'rgb(80, 120, 210)';
		context.fillStyle = 'rgba(100, 140, 230, 0.5)';
		context.fillText(text, canvas.width/2 - metrics.width/2, canvas.height/2);
		context.strokeText(text, canvas.width/2 - metrics.width/2, canvas.height/2);

		context.restore();
	}

	// 绘制指引线
	function drawGuidewire(){
		context.moveTo(ball.left, ball.top);
		context.lineTo(lastMouse.left, lastMouse.top);
		context.stroke();
	}

	// 更新显示文字
	function updateBackgroundText(){
		var text;

		if(lastScore === 3){
			text = 'Three pointer!';
		}else if(lastScore === 2){
			text = 'Nice shot!';
		}else if(needInstructions){
			text = 'Click to launch ball';
		}

		showText(text);
	}

	// 一秒后重置最新得分
	function resetScoreLater(){
		setTimeout(function(){
			lastScore = 0;
		}, 1000);
	}

	// 更新所有精灵
	function updateSprites(time){
		bucket.update(context, time);
		launchPad.update(context, time);
		ball.update(context, time);
	}

	// 绘制所有精灵
	function paintSprites(){
		bucket.paint(context);
		launchPad.paint(context);
		ball.paint(context);
	}

// Event handlers......................................................................

	// 鼠标按下
	// 设置小球的速度
	// 判断小球为飞行状态
	// 记录发射时间
	canvas.onmousedown = function(e){
		e.preventDefault();

		if(!ballInFlight){
			ball.velocityX = launchVelocity * Math.cos(launchAngle);
			ball.velocityY = launchVelocity * Math.sin(launchAngle);
			ballInFlight = true;
			threePointer = false;
			launchTime = +new Date();
		}
	};

	// 鼠标移动
	// 更新鼠标的位置
	// 计算发射角度，发射速度
	// 输出发射角度，发射速度
	canvas.onmousemove = function(e){
		e.preventDefault();
		
		var loc;

		if(!ballInFlight){
			loc = windowToCanvas(e.clientX, e.clientY);
			lastMouse.left = loc.x;
			lastMouse.top = loc.y;

			deltaX = Math.abs(lastMouse.left - ball.left);
			deltaY = Math.abs(lastMouse.top - ball.top);

			launchAngle = Math.atan(parseFloat(deltaY) / parseFloat(deltaX));
			launchVelocity = 4 * deltaY / Math.sin(launchAngle) / pixelsPerMeter;

			launchVelocityOutput.innerHTML = launchVelocity.toFixed(2);
			launchAngleOutput.innerHTML = (launchAngle * 180 / Math.PI).toFixed(2);
		}
	};

// Animation loop.........................................................................

	// 动画
	function animate(time){
		time = +new Date();
		elapsedTime = (time - launchTime) / 1000; // 计算从发射开始经过的时间--转换到秒
		context.clearRect(0, 0, canvas.width, canvas.height); // 清空画布

		// 如果小球还未起飞，绘制引线，绘制提示文字
		if(!ballInFlight){
			drawGuidewire();
			updateBackgroundText();

			// 如果上次得分不为0，重置
			if(lastScore !== 0){ // Just scored
				resetScoreLater();
			}
		}

		// 更新精灵并绘制
		updateSprites(time);
		paintSprites();

		window.requestNextAnimationFrame(animate);
	}

// Initialization..........................................................................

	ball.width = BALL_RADIUS*2;
	ball.height = ball.width;
	ball.left = LAUNCHPAD_X + LAUNCHPAD_WIDTH/2;
	ball.top = LAUNCHPAD_Y - ball.height/2;
	ball.radius = BALL_RADIUS;

	context.lineWidth = 0.5;
	context.strokeStyle = 'rgba(0, 0, 0, 0.5)';
	context.shadowColor = 'rgba(0, 0, 0, 0.5)';
	context.shadowOffsetX = 2;
	context.shadowOffsetY = 2;
	context.shadowBlue = 4;
	context.stroke();

	bucketImage.src = 'bucket.jpg';
	bucketImage.onload = function(e){
		bucket.left = BUCKET_X;
		bucket.top = BUCKET_Y;
		bucket.width = bucketImage.width;
		bucket.height = bucketImage.height;
	};

	animate();